/*  ----------------------------------------------------------------------------
 *  Arello Mobile
 *  Fast 3d Engine
 *  ----------------------------------------------------------------------------
 * Except where otherwise noted, this work is licensed under
 * http://creativecommons.org/licenses/by/3.0
 */

#ifndef __MATRIX_H__
#define __MATRIX_H__

#include "Vector.h"

namespace liba
{
namespace math
{

template<typename T, int dim>
class MatrixSquare
{
	typedef Vector<T, dim*dim> vector_type;
	typedef MatrixSquare<T, dim> this_type;
	vector_type impl;
public:
/*	MatrixSquare( const vector_type & vec )
		:	vector_type( vec )
	{}*/
	MatrixSquare()
	{}
	
	float * data()
	{
		return impl.data();
	}
	const float * data()const
	{
		return impl.data();
	}
	
	T & rc(size_t row, size_t column)
	{
		return impl.data(column + row*dim);
	}
	T rc(size_t row, size_t column)const
	{
		return impl.data(column + row*dim);
	}
	T & cr(size_t column, size_t row)
	{
		return impl.data(column + row*dim);
	}
	T cr(size_t column, size_t row)const
	{
		return impl.data(column + row*dim);
	}
/*	this_type operator- ()const
	{
		this_type mat;
		mat.impl = -impl;
		return mat;
	}*/
	bool operator== ( const this_type & mat )const
	{
		return impl == mat.impl;
	}
	bool operator!= ( const this_type & mat )const
	{
		return !operator==( mat );
	}
	const this_type & operator+= ( const this_type & mat )
	{
		impl += mat.impl;
		return *this;
	}
	const this_type & operator-= ( const this_type & mat )
	{
		impl -= mat.impl;
		return *this;
	}
	const this_type & operator*= ( const T & f )
	{
		impl *= f;
		return *this;
	}
	const this_type & operator/= ( const T & f )
	{
		impl /= f;
		return *this;
	}
	const this_type & operator*= ( const this_type & m )
	{
		this_type tmp = *this;
		for( int row=0; row < dim; ++row )
		{
			for( int column=0; column < dim; ++column )
			{
				T sum(0);
				for( int i=0; i < dim; ++i )
				{
					sum += tmp.rc(row,i) * m.rc(i,column);
				}
				rc(row,column) = sum;
			}
		}
		return *this;
	}
	static this_type identity()
	{
		this_type tmp;
		for( size_t row=0; row < dim; ++row )
			for( size_t column=0; column < dim; ++column )
				tmp.rc(row,column) = (row == column) ? T(1) : T(0);
		return tmp;
	}
/*	static void assign_mult( this_type * tmp, const this_type & u, const this_type & v )
	{
		for( int row=0; row < dim; ++row )
		{
			for( int column=0; column < dim; ++column )
			{
				T sum(0);
				for( int i=0; i < dim; ++i )
				{
					s += u.rc(row,i) * v.rc(i,column);
				}
				tmp->rc(row,column) = s;
			}
		}
	}*/
	void transpose()
	{
		for( size_t row=0; row < dim; ++row )
			for( size_t column=row + 1; column < dim; ++column )
			{
				T s = rc( row, column );
				rc( row, column ) = rc( column, row );
				rc( column, row ) = s;
			}
	}
	static this_type zero()
	{
		this_type mat;
		for(size_t i=0; i < dim * dim; ++i)
			mat.impl.data(i) = 0;
		return mat;
	}

	template<typename Sym>
	struct ConverterBuffer
		:	public converter::ConverterStaticBuffer<Sym, 20*dim*dim>
	{
		typedef converter::ConverterStaticBuffer<Sym, 20*dim*dim> StaticBuffer;
		
		bool convert(const this_type & value)
		{
			StaticBuffer::size = converter::simple_print( StaticBuffer::data, value.impl[0] );
			for( size_t i = 1; i < dim*dim; ++i )
			{
				StaticBuffer::data[StaticBuffer::size++] = ' ';
				StaticBuffer::size += converter::simple_print( StaticBuffer::data + StaticBuffer::size, value.impl[i] );
			}
			return true;
		}
	};

	template<class Sym>
	bool convert(const Sym * be, const Sym * en)
	{
		const Sym * beg = be;
		for( int i = 0; i < dim; ++i )
		{
			for(size_t k = 0; k < dim; ++k)
			{
				beg = converter::simple_convert( beg, en, &impl[i + k*dim]);
			if( !beg )
				return false;
		}
		}
		return true;
	}
};

template< typename T, int dim>
MatrixSquare<T, dim> operator* ( const MatrixSquare<T, dim> & u, const MatrixSquare<T, dim> & v )
{
	return MatrixSquare<T, dim>( u ) *= v;
}


template< typename T, int dim>
Vector<T, dim> operator * ( const MatrixSquare<T, dim> & mat, const Vector<T, dim> & v )
{
	Vector<T, dim> tmp;
	for( size_t row=0; row < dim; ++row )
	{
		T sum(0);
		for( size_t column=0; column < dim; ++column )
		{
			sum += mat.rc(row,column) * v.data(column);
		}
		tmp.data(row) = sum;
	}
	return tmp;
}

template< typename T, int dim>
Vector<T, dim> operator* ( const Vector<T, dim> & v, const MatrixSquare<T, dim> & mat )
{
	Vector<T, dim> tmp;
	for( size_t column=0; column < dim; ++column )
	{
		T sum(0);
		for( size_t row=0; row < dim; ++row )
		{
			sum += mat.rc(row,column) * v.data(row);
		}
		tmp.data(column) = sum;
	}
	return tmp;
}

template< typename T, int dim>
MatrixSquare<T, dim> operator+ ( const MatrixSquare<T, dim> & u, const MatrixSquare<T, dim> & v )
{
	return MatrixSquare<T,dim>( u ) += v;
}

template< typename T, int dim>
MatrixSquare<T, dim> operator- ( const MatrixSquare<T, dim> & u, const MatrixSquare<T, dim> & v )
{
	return MatrixSquare<T,dim>( u ) -= v;
}

template< typename T, int dim>
MatrixSquare<T, dim> operator* ( const MatrixSquare<T, dim> & u, const T & v )
{
	return MatrixSquare<T,dim>( u ) *= v;
}

template< typename T, int dim>
MatrixSquare<T, dim> operator/ ( const MatrixSquare<T, dim> & u, const T & v )
{
	return MatrixSquare<T,dim>( u ) /= v;
}

}  // namespace math
}  // namespace liba
using namespace liba;


#endif //__MATRIX_H__